package com.java.util;

import com.java.util.pojo.BudgetPlanPO;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 根据集合中某个字段去重
 */
public class ListDistinct {
    private static final String PORTFOLIO_LEVEL = "PORTFOLIO";
    private static final String HOTEL_LEVEL = "HOTEL";

    public static void main(String[] args) {

        List<BudgetPlanPO> list = new ArrayList<>();

        for (int i = 0; i < 10; i++) {

            if (i % 2 == 0) {
                BudgetPlanPO build = BudgetPlanPO.builder().campaignUuidDs(111111111111L).hotelId((long) i).build();
                list.add(build);
            } else if (i % 3 == 0) {
                BudgetPlanPO build = BudgetPlanPO.builder().campaignUuidDs(222222222222L).hotelId((long) i).build();
                list.add(build);
            } else {
                BudgetPlanPO build = BudgetPlanPO.builder().campaignUuidDs(333333333333L).hotelId((long) i).build();
                list.add(build);
            }
        }

        BudgetPlanPO build = BudgetPlanPO.builder().campaignUuidDs(333333333333L).hotelId((long) 7).build();
        list.add(build);

        // 写法一：基于getCampaignUuidDs去重
        List<BudgetPlanPO> budgetPlanList0 = list.stream()
//                .filter(item -> PORTFOLIO_LEVEL.equals(item.getLevel()))
                .collect(
                        Collectors.toMap(
                                BudgetPlanPO::getCampaignUuidDs,
                                Function.identity(),
                                (existing, replacement) -> existing
                        ))
                .values()
                .stream()
                .toList();
        // 写法二：基于getCampaignUuidDs去重
        List<BudgetPlanPO> budgetPlanList1 = list.stream()
//                .filter(item -> PORTFOLIO_LEVEL.equals(item.getLevel()))
                .collect(Collectors.collectingAndThen(
                        Collectors.toMap(
                                BudgetPlanPO::getCampaignUuidDs,
                                Function.identity(),
                                (a, b) -> a
                        ),
                        map -> new ArrayList<>(map.values())
                ));


        // 写法一，先基于getCampaignUuidDs分组，然后基于getHotelId去重
        Map<Long, List<BudgetPlanPO>> collect = list.stream()
//                .filter(item -> HOTEL_LEVEL.equals(item.getLevel()))
                .collect(Collectors.groupingBy(
                        BudgetPlanPO::getCampaignUuidDs,
                        Collectors.collectingAndThen(
                                Collectors.toMap(
                                        BudgetPlanPO::getHotelId,
                                        Function.identity(),
                                        (existing, replacement) -> existing
                                ),
                                map -> map.values().stream().toList()
                        )
                ));

        // 写法二，比较简介的
        Map<Long, List<BudgetPlanPO>> result = list.stream()
//                .filter(item -> HOTEL_LEVEL.equals(item.getLevel()))
                .collect(Collectors.groupingBy(
                        BudgetPlanPO::getCampaignUuidDs,
                        Collectors.toMap(
                                BudgetPlanPO::getHotelId,
                                Function.identity(),
                                (a, b) -> a
                        )
                ))
                .entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        e -> new ArrayList<>(e.getValue().values())
                ));

        System.out.println(">>>>>>>>>>>>>>>> ");


        // 下面是基于getCampaignUuidDs和getHotelId去重的两种写法

        /**
         * Collectors.collectingAndThen 是 Java Stream API 中的一个收集器，它允许你在常规收集操作完成后，立即对结果进行额外的转换处理。它的核心思想是："先收集，然后转换"。
         *
         * 1. 第一个参数：下游收集器 (downstream)
         * 传递任何标准的 Collector 实现：
         * Collectors.toList()
         * Collectors.toSet()
         * Collectors.toMap()
         * Collectors.groupingBy()
         * 其他自定义收集器
         *
         * 2. 第二个参数：终结函数 (finisher)
         * 传入一个 Function，接收下游收集器的结果，返回最终转换后的值：
         * 可以直接用方法引用（如 Collections::unmodifiableList）
         * 可以用 Lambda 表达式（如 map -> new ArrayList<>(map.values())）
         */
        List<BudgetPlanPO> dedupedList0 = list.stream()
//                .filter(item -> HOTEL_LEVEL.equals(item.getLevel()))
                .collect(Collectors.collectingAndThen(
                        // 第一步：先收集成Map
                        Collectors.toMap(
                                // Key: 拼接复合键
                                item -> item.getCampaignUuidDs() + "_" + item.getHotelId(),
                                // Value: 对象本身
                                Function.identity(),
                                // 合并规则：key冲突时保留旧值
                                (a, b) -> a
                        ),
                        // 第二步：将Map的values转为List
                        map -> new ArrayList<>(map.values())
                ));


        /**
         * Collectors.groupingBy 是 Java Stream API 中用于按条件分组的核心收集器，功能类似 SQL 的 GROUP BY。它支持单级分组、多级嵌套分组、分组后聚合操作等场景。下面详细说明其用法和参数传递方式。
         *
         * groupingBy(Function)	按分类函数分组，值存到List。
         * groupingBy(Function, Collector)	分组后对值进一步处理。
         * groupingBy(Function, Supplier, Collector)	自定义返回的Map类型。比如将分组结果存到TreeMap。
         *
         * 三、进阶用法（多参数）
         * 1. 分组后聚合（第二参数）
         * 案例1：计算每个部门的平均工资
         * Map<String, Double> avgSalaryByDept = employees.stream()
         *     .collect(groupingBy(
         *         Employee::getDepartment,
         *         Collectors.averagingDouble(Employee::getSalary)
         *     ));
         *
         * 案例2：分组后统计数量
         * Map<String, Long> countByDept = employees.stream()
         *     .collect(groupingBy(
         *         Employee::getDepartment,
         *         Collectors.counting()
         *     ));
         *
         * 案例3：先按部门，再按职位分组
         * Map<String, Map<String, List<Employee>>> byDeptAndJob = employees.stream()
         *     .collect(groupingBy(
         *         Employee::getDepartment,
         *         groupingBy(Employee::getJobTitle)
         *     ));
         *
         * 3. 自定义Map容器（第三参数）
         * 分组结果存入TreeMap（自动排序）
         * Map<String, List<Employee>> sortedMap = employees.stream()
         *     .collect(groupingBy(
         *         Employee::getDepartment,
         *         TreeMap::new,          // 指定Map实现类
         *         toList()
         *     ));
         *
         */
        List<BudgetPlanPO> dedupedList1 = list.stream()
//                .filter(item -> HOTEL_LEVEL.equals(item.getLevel()))
                .collect(
                        // 基于getCampaignUuidDs分组，整体转成了一个Map<Long, Map<Long, BudgetPlanPO>>类型
                        Collectors.groupingBy(
                                BudgetPlanPO::getCampaignUuidDs,
                                // 分组后对值进一步处理。对每个分组内的BudgetPlanPO对象，基于getHotelId去重
                                // 最终转成了一个Map<Long, BudgetPlanPO>类型
                                Collectors.toMap(
                                        BudgetPlanPO::getHotelId,
                                        Function.identity(),
                                        (a, b) -> a
                                )
                        ))
                // 获取entry集合
                .entrySet()
                .stream()
                // 将每个entry的value转换为List<BudgetPlanPO>，最终将Map的values转为List<BudgetPlanPO>
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> new ArrayList<>(entry.getValue().values())
                ))
                .values()
                .stream()
                // flatMap将 Stream<List<BudgetPlanPO>> 转换为单一的 Stream<BudgetPlanPO>
                .flatMap(Collection::stream)
                .toList();

    }
}
